// ****************************************************************
// Copyright 2012, Stephan Burguchev
// e-mail: me@sburg.net
// ****************************************************************
// *
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Updater.Common;
using Version = Updater.Utils.Version;

namespace Updater.Updates
{
    public class UpdatesRssFeedReader : IUpdatesFeedReader
    {
        private XNamespace _updatesRssFeedSchema;

        public UpdatesRssFeedReader()
        {
            this.UpdatesRssFeedSchema = "http://sburg.net/trv/xml-namespaces/updates-feed";
        }

        public string UpdatesRssFeedSchema
        {
            get
            {
                return this._updatesRssFeedSchema.NamespaceName;
            }
            set
            {
                this._updatesRssFeedSchema = XNamespace.Get(value);
            }
        }

        public UpdateInformation[] Read(Stream feed)
        {
            if (feed == null)
            {
                throw new ArgumentNullException("feed");
            }

            try
            {
                XDocument xdoc = XDocument.Load(feed);

                UpdateInformation[] updatesInfo =
                    xdoc.Root.Element("channel").Elements("item").Select(this.parseUpdateItem)
                        .OrderBy(u => u.Version).ToArray();

                return updatesInfo;
            }
            catch (Exception ex)
            {
                throw new UpdatesFeedReaderException("Unable to read an updates RSS-feed.", ex);
            }
        }

        private UpdateInformation parseUpdateItem(XElement item)
        {
            XNamespace ns = this._updatesRssFeedSchema;

            // version retrieving
            string versionStr = item.Attribute(ns + "version").Value;
            Version version = Version.Parse(versionStr);

            // min-version retrieving
            XAttribute minVersionAttr = item.Element("enclosure").Attribute(ns + "min-version");
            Version minVersion = null;
            if (minVersionAttr != null)
            {
                minVersion = Version.Parse(minVersionAttr.Value);
            }

            // Update type retrieving
            string updateTypeStr = item.Element("enclosure").Attribute(ns + "update-type").Value;
            Type updateType = Type.GetType(updateTypeStr);

            // checksum retrieving
            byte[] checksum = null;
            XAttribute checksumAttr = item.Element("enclosure").Attribute(ns + "checksum");
            if (checksumAttr != null)
            {
                checksum = Convert.FromBase64String(checksumAttr.Value);
            }

            // checksum-type retrieving
            ChecksumType? checksumType = null;
            XAttribute checksumTypeAttr = item.Element("enclosure").Attribute(ns + "checksum-type");
            if (checksumTypeAttr != null)
            {
                checksumType = (ChecksumType)Enum.Parse(typeof(ChecksumType), checksumTypeAttr.Value, true);
            }
            else if (checksumAttr != null)
            {
                throw new FormatException("A checksum for an update enclosure has been specified but a checksum type has not.");
            }

            return new UpdateInformation(
                item.Element("title").Value,
                item.Element("description").Value,
                minVersion,
                version,
                DateTime.Parse(item.Element("pubDate").Value, CultureInfo.InvariantCulture),
                new Uri(item.Element("enclosure").Attribute("url").Value),
                int.Parse(item.Element("enclosure").Attribute("length").Value),
                updateType,
                checksum,
                checksumType);
        }
    }
}
